package com.zg.indiv.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zg.common.core.dao.indiv.*;
import com.zg.common.core.dao.indiv.entity.*;
import com.zg.common.core.exception.BusinessException;
import com.zg.indiv.constants.IndivConstant;
import com.zg.indiv.constants.IndivExceptionEnum;
import com.zg.indiv.domain.req.selector.*;
import com.zg.indiv.domain.vo.IndivSelectorFormParamVo;
import com.zg.indiv.domain.vo.IndivSelectorFormVo;
import com.zg.indiv.domain.vo.IndivSelectorUseVo;
import com.zg.indiv.domain.vo.IndivSelectorVo;
import com.zg.indiv.service.IndivSelectorService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lixin
 */
@Service
@RequiredArgsConstructor
public class IndivSelectorServiceImpl implements IndivSelectorService {
    private final IndivSelectorDao indivSelectorDao;
    private final IndivSelectorFormDao indivSelectorFormDao;
    private final IndivSelectorFormParamDao indivSelectorFormParamDao;
    private final IndivSelectorUseDao indivSelectorUseDao;
    private final IndivFolderDao indivFolderDao;

    @Override
    public IPage<IndivSelector> page(IndivSelectorPageReq param) {
        LambdaQueryWrapper<IndivSelector> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        String searchStr = param.getSearchStr();
        lambdaQueryWrapper.like(StrUtil.isNotBlank(searchStr), IndivSelector::getSearchStr, "%" + searchStr + "%");
        lambdaQueryWrapper.orderByAsc(IndivSelector::getUpdateTime);
        return indivSelectorDao.page(
                new Page<>(param.getPage(), param.getPageSize()),
                lambdaQueryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(IndivSelectorAddReq param) {
        long nameCount = indivSelectorDao.count(
                new LambdaQueryWrapper<IndivSelector>()
                        .eq(IndivSelector::getName, param.getName()));
        if (nameCount > 0) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_NAME_ERROR.getCode(),
                    IndivExceptionEnum.SELECTOR_NAME_ERROR.getMsg());
        }

        IndivSelector indivSelector = new IndivSelector();
        indivSelector.setName(param.getName());
        indivSelector.setSearchStr(param.getName());
        indivSelectorDao.save(indivSelector);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(IndivSelectorEditReq param) {
        IndivSelector indivSelector = indivSelectorDao.getById(param.getSelectorId());
        if (indivSelector == null) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_NO.getMsg());
        }

        if (!indivSelector.getName().equals(param.getName())) {
            long nameCount = indivSelectorDao.count(
                    new LambdaQueryWrapper<IndivSelector>()
                            .eq(IndivSelector::getName, param.getName()));
            if (nameCount > 0) {
                BusinessException.happen(IndivExceptionEnum.SELECTOR_NAME_ERROR.getCode(),
                        IndivExceptionEnum.SELECTOR_NAME_ERROR.getMsg());
            }

            indivSelector.setName(param.getName());
            indivSelector.setSearchStr(param.getName());
            indivSelectorDao.updateById(indivSelector);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(List<Long> idList) {
        long useCount = indivSelectorUseDao.count(
                new LambdaQueryWrapper<IndivSelectorUse>()
                        .in(IndivSelectorUse::getSelectorId, idList)
        );
        if (useCount > 0) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_USE_DEL_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_USE_DEL_NO.getMsg());
        }

        indivSelectorDao.removeBatchByIds(idList);
        indivSelectorFormDao.remove(
                new LambdaQueryWrapper<IndivSelectorForm>()
                        .in(IndivSelectorForm::getSelectorId, idList)
        );
        indivSelectorFormParamDao.remove(
                new LambdaQueryWrapper<IndivSelectorFormParam>()
                        .in(IndivSelectorFormParam::getSelectorId, idList)
        );
    }

    @Override
    public IndivSelectorVo get(IndivSelectorGetReq param) {
        IndivSelector indivSelector = indivSelectorDao.getById(param.getSelectorId());
        if (indivSelector == null) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_NO.getMsg());
        }

        IndivSelectorVo indivSelectorVo = BeanUtil.copyProperties(indivSelector, IndivSelectorVo.class);
        List<IndivSelectorForm> indivSelectorForms = indivSelectorFormDao.list(
                new LambdaQueryWrapper<IndivSelectorForm>()
                        .eq(IndivSelectorForm::getSelectorId, param.getSelectorId())
        );
        List<IndivSelectorFormVo> indivSelectorFormList = BeanUtil
                .copyToList(indivSelectorForms, IndivSelectorFormVo.class);
        indivSelectorVo.setIndivSelectorFormList(indivSelectorFormList);
        Map<Long, IndivSelectorFormVo> indivSelectorFormMap = indivSelectorFormList.stream()
                .collect(Collectors.toMap(IndivSelectorFormVo::getId, obj -> obj));

        List<IndivSelectorFormParam> indivSelectorFormParams = indivSelectorFormParamDao.list(
                new LambdaQueryWrapper<IndivSelectorFormParam>()
                        .eq(IndivSelectorFormParam::getSelectorId, param.getSelectorId())
        );
        List<IndivSelectorFormParamVo> indivSelectorFormParamVos = BeanUtil
                .copyToList(indivSelectorFormParams, IndivSelectorFormParamVo.class);
        for (IndivSelectorFormParamVo selectorFormParam : indivSelectorFormParamVos) {
            IndivSelectorFormVo selectorForm = indivSelectorFormMap.get(selectorFormParam.getSelectorFormId());
            List<IndivSelectorFormParamVo> selectorFormParamList = selectorForm.getSelectorFormParamList();
            if (selectorFormParamList == null) {
                selectorFormParamList = new ArrayList<>();
            }
            selectorFormParamList.add(selectorFormParam);
            selectorForm.setSelectorFormParamList(selectorFormParamList);
        }

        return indivSelectorVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addForm(IndivSelectorAddFormReq param) {
        IndivSelector indivSelector = indivSelectorDao.getById(param.getSelectorId());
        if (indivSelector == null) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_NO.getMsg());
        }

        IndivFolder indivFolder = indivFolderDao.getById(param.getFolderId());
        if (indivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NO.getCode(),
                    IndivExceptionEnum.FOLDER_NO.getMsg());
        }

        Long parentSelectorFormId = param.getParentSelectorFormId();
        List<IndivSelectorFormParamReq> formParamList = param.getFormParamList();
        if (parentSelectorFormId == null
                || IndivConstant.INDIV_SELECTOR_FORM_PARENT_ID.equals(parentSelectorFormId)){
            if (formParamList != null && formParamList.size() > 0) {
                BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_PARAM_NO.getCode(),
                        IndivExceptionEnum.SELECTOR_FORM_PARAM_NO.getMsg());
            }
            param.setParentSelectorFormId(IndivConstant.INDIV_SELECTOR_FORM_PARENT_ID);
        } else {
            IndivSelectorForm parentSelectorForm = indivSelectorFormDao.getById(param.getParentSelectorFormId());
            if (parentSelectorForm == null) {
                BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_PARENT_NO.getCode(),
                        IndivExceptionEnum.SELECTOR_FORM_PARENT_NO.getMsg());
            }
        }

        IndivSelectorForm indivSelectorForm = BeanUtil.copyProperties(param, IndivSelectorForm.class);
        indivSelectorForm.setParentId(param.getParentSelectorFormId());
        indivSelectorFormDao.save(indivSelectorForm);

        saveFormParam(param.getSelectorId(), indivSelectorForm.getId(), formParamList);

    }

    private void saveFormParam(Long selectorId,
                               Long selectorFormId,
                               List<IndivSelectorFormParamReq> formParamList) {
        if (formParamList != null && formParamList.size() > 0) {
            Map<String, IndivSelectorFormParamReq> paramNameMap = new HashMap<>();
            Map<String, IndivSelectorFormParamReq> targetParamMap = new HashMap<>();
            List<IndivSelectorFormParam> addFormParams = new ArrayList<>();
            for (IndivSelectorFormParamReq formParamReq : formParamList) {
                if (paramNameMap.containsKey(formParamReq.getParamName())) {
                    BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_PARAM_NAME_ERROR.getCode(),
                            IndivExceptionEnum.SELECTOR_FORM_PARAM_NAME_ERROR.getMsg());
                }
                paramNameMap.put(formParamReq.getParamName(), formParamReq);

                if(targetParamMap.containsKey(formParamReq.getTargetParam())) {
                    BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_TARGET_PARAM_ERROR.getCode(),
                            IndivExceptionEnum.SELECTOR_FORM_TARGET_PARAM_ERROR.getMsg());
                }
                targetParamMap.put(formParamReq.getTargetParam(), formParamReq);

                IndivSelectorFormParam addFormParam = BeanUtil.copyProperties(formParamReq,
                        IndivSelectorFormParam.class);
                addFormParam.setSelectorId(selectorId);
                addFormParam.setSelectorFormId(selectorFormId);
                addFormParams.add(addFormParam);
            }
            indivSelectorFormParamDao.saveBatch(addFormParams);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editForm(IndivSelectorEditFormReq param) {
        IndivSelectorForm indivSelectorForm = indivSelectorFormDao.getById(param.getSelectorFormId());
        if (indivSelectorForm == null) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_FORM_NO.getMsg());
        }

        IndivFolder indivFolder = indivFolderDao.getById(param.getFolderId());
        if (indivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NO.getCode(),
                    IndivExceptionEnum.FOLDER_NO.getMsg());
        }

        List<IndivSelectorFormParamReq> formParamList = param.getFormParamList();
        Long parentSelectorFormId = param.getParentSelectorFormId();
        if (parentSelectorFormId == null
                || IndivConstant.INDIV_SELECTOR_FORM_PARENT_ID.equals(parentSelectorFormId)){
            if (formParamList != null && formParamList.size() > 0) {
                BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_PARAM_NO.getCode(),
                        IndivExceptionEnum.SELECTOR_FORM_PARAM_NO.getMsg());
            }
            param.setParentSelectorFormId(IndivConstant.INDIV_SELECTOR_FORM_PARENT_ID);
        } else {
            IndivSelectorForm parentSelectorForm = indivSelectorFormDao.getById(param.getParentSelectorFormId());
            if (parentSelectorForm == null) {
                BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_PARENT_NO.getCode(),
                        IndivExceptionEnum.SELECTOR_FORM_PARENT_NO.getMsg());
            }
        }

        IndivSelectorForm newSelectorForm = BeanUtil.copyProperties(param, IndivSelectorForm.class);
        indivSelectorFormDao.updateById(newSelectorForm);

        indivSelectorFormParamDao.remove(
                new LambdaQueryWrapper<IndivSelectorFormParam>()
                        .eq(IndivSelectorFormParam::getSelectorFormId, param.getSelectorFormId())
        );
        saveFormParam(indivSelectorForm.getSelectorId(), param.getSelectorFormId(), formParamList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeForm(List<Long> idList) {
        List<IndivSelectorForm> indivSelectorForms = indivSelectorFormDao.listByIds(idList);
        if (indivSelectorForms.size() < 1) {
            return;
        }
        Set<Long> selectorIds = new HashSet<>();
        Set<Long> formIds = new HashSet<>();
        for (IndivSelectorForm selectorForm : indivSelectorForms) {
            selectorIds.add(selectorForm.getSelectorId());
            formIds.add(selectorForm.getFormId());
        }

        long selectorUseCount = indivSelectorUseDao.count(
                new LambdaQueryWrapper<IndivSelectorUse>()
                        .in(IndivSelectorUse::getSelectorId, selectorIds)
                        .in(IndivSelectorUse::getFormId, formIds)
        );
        if (selectorUseCount > 0) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_ERROR.getCode(),
                    IndivExceptionEnum.SELECTOR_FORM_ERROR.getMsg());
        }

        indivSelectorFormDao.removeBatchByIds(idList);
        indivSelectorFormParamDao.remove(
                new LambdaQueryWrapper<IndivSelectorFormParam>()
                        .in(IndivSelectorFormParam::getSelectorFormId, idList)
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUseState(IndivSelectorAddUseStateReq param) {
        IndivSelector indivSelector = indivSelectorDao.getById(param.getSelectorId());
        if (indivSelector == null) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_NO.getMsg());
        }

        long selectorFormCount = indivSelectorFormDao.count(
                new LambdaQueryWrapper<IndivSelectorForm>()
                        .eq(IndivSelectorForm::getSelectorId, param.getSelectorId())
                        .eq(IndivSelectorForm::getFormId, param.getFormId())
        );
        if (selectorFormCount < 1) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_FORM_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_FORM_NO.getMsg());
        }

        Integer useType = param.getUseType();
        if (!IndivConstant.INDIV_SELECTOR_USE_TYPE_FORM.equals(useType)
                && !IndivConstant.INDIV_SELECTOR_USE_TYPE_FLOW.equals(useType)) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_USE_TYPE_NO.getCode(),
                    IndivExceptionEnum.SELECTOR_USE_TYPE_NO.getMsg());
        }
        if (IndivConstant.INDIV_SELECTOR_USE_TYPE_FLOW.equals(useType)) {
            param.setUseFormId(null);
        }

        long selectUseCount = indivSelectorUseDao.count(
                new LambdaQueryWrapper<IndivSelectorUse>()
                        .eq(IndivSelectorUse::getSelectorId, param.getSelectorId())
                        .eq(IndivSelectorUse::getFormId, param.getFormId())
                        .eq(IndivSelectorUse::getFormDataId, param.getFormDataId())
                        .eq(IndivSelectorUse::getUseType, param.getUseType())
                        .eq(IndivSelectorUse::getUseFormId, param.getUseFormId())
                        .eq(IndivSelectorUse::getUseDataId, param.getUseDataId())
        );
        if (selectUseCount > 0) {
            BusinessException.happen(IndivExceptionEnum.SELECTOR_DATA_ERROR.getCode(),
                    IndivExceptionEnum.SELECTOR_DATA_ERROR.getMsg());
        }

        IndivSelectorUse indivSelectorUse = BeanUtil.copyProperties(param, IndivSelectorUse.class);
        indivSelectorUseDao.save(indivSelectorUse);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeUseState(IndivSelectorRemoveUseStateReq param) {
        if (IndivConstant.INDIV_SELECTOR_USE_TYPE_FLOW.equals(param.getUseType())) {
            param.setUseFormId(null);
        }

        indivSelectorUseDao.remove(
                new LambdaQueryWrapper<IndivSelectorUse>()
                        .eq(IndivSelectorUse::getSelectorId, param.getSelectorId())
                        .eq(IndivSelectorUse::getFormId, param.getFormId())
                        .eq(IndivSelectorUse::getFormDataId, param.getFormDataId())
                        .eq(IndivSelectorUse::getUseType, param.getUseType())
                        .eq(param.getUseFormId() != null, IndivSelectorUse::getUseFormId, param.getUseFormId())
                        .eq(IndivSelectorUse::getUseDataId, param.getUseDataId())
        );
    }

    @Override
    public IPage<IndivSelectorUseVo> pageSelectorUse(IndivSelectorUsePageReq param) {
        LambdaQueryWrapper<IndivSelectorUse> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(IndivSelectorUse::getSelectorId, param.getSelectorId());
        Page<IndivSelectorUse> page = indivSelectorUseDao.page(
                new Page<>(param.getPage(), param.getPageSize()),
                lambdaQueryWrapper);
        IPage<IndivSelectorUseVo> result = new Page<>();
        BeanUtil.copyProperties(page, result);
        return result;
    }
}
